/**
 * Well, this class is useless now that google.maps.Polygon object has a setEditable function
 * that does exactly what this class tried to do.
 * 
 * I leave the code here, in case someone wants to see and study it.
 * 
 * The one thing that will remain is contains function, which is still not present in Polygon object, but 
 * as a function of google.maps.Polygon object.
 * 
 * Cheers.
 *

if(!anthill) var anthill = {};
if(!anthill.maps) anthill.maps = {};

anthill.maps.Polygon = function(options) {
  this.set('polygon', new google.maps.Polygon(options));
  this.get('polygon').bindTo('map', this);
  if(options.editable === false) {
    this.set('editable', false);
  } else {
    this.set('editable', true);
  }
  if(options.edit && this.get('editable')) {
    if(options.map) {
      this.set('edit_map', options.map);
    } else {
      this.set('edit_map', null);
    }
    this.set('editing', true);
  } else {
    this.set('edit_map', null);
    this.set('editing', false);
  }
  this.set('axis', new google.maps.MVCArray());
  this.set('axis_tmp', new google.maps.MVCArray());
  //this.polygon.bindTo('latLngs', this);
  
  this.editVertex = new google.maps.MarkerImage('img/vertice.png',
						    new google.maps.Size(11, 11),
						    new google.maps.Point(0,0),
						    new google.maps.Point(6,6));
  this.tmpVertex = new google.maps.MarkerImage('img/vertice_tmp.png',
						new google.maps.Size(11, 11),
						new google.maps.Point(0,0),
						new google.maps.Point(6,6));
  
  this.preparePaths();
  
  google.maps.event.trigger(this.get('polygon').getPaths(), 'length_changed');
  
  this.get('polygon').getPaths().forEach(function(path, index) {
    google.maps.event.trigger(path, 'length_changed');
  });
  
  var p = this;
  google.maps.event.addListener(this.get('polygon'), 'click', function(evt) {
    google.maps.event.trigger(p, 'click', evt);
  });
  google.maps.event.addListener(this.get('polygon'), 'dblclick', function(evt) {
    google.maps.event.trigger(p, 'dblclick', evt);
  });
  google.maps.event.addListener(this.get('polygon'), 'mousedown', function(evt) {
    google.maps.event.trigger(p, 'mousedown', evt);
  });
  google.maps.event.addListener(this.get('polygon'), 'mouseup', function(evt) {
    google.maps.event.trigger(p, 'mouseup', evt);
  });
  google.maps.event.addListener(this.get('polygon'), 'mousemove', function(evt) {
    google.maps.event.trigger(p, 'mousemove', evt);
  });
  google.maps.event.addListener(this.get('polygon'), 'mouseout', function(evt) {
    google.maps.event.trigger(p, 'mouseout', evt);
  });
  google.maps.event.addListener(this.get('polygon'), 'mouseover', function(evt) {
    google.maps.event.trigger(p, 'mouseover', evt);
  });
  google.maps.event.addListener(this.get('polygon'), 'rightclick', function(evt) {
    google.maps.event.trigger(p, 'rightclick', evt);
  });
  
  if(this.get('polygon').getPaths().length == 0) {
    this.new();
  }
};

anthill.maps.Polygon.prototype = new google.maps.MVCObject();

anthill.maps.Polygon.prototype.getPosition = function(elem, array) {
  for(var i = 0; i<array.getLength(); i++) {
    if(elem == array.getAt(i)) {
      return i;
    }
  }
  return false;
};

anthill.maps.Polygon.prototype.new = function(sub) {
  if(!sub && sub!=0) {
    this.setPaths(new google.maps.MVCArray());
    sub = -1;
  }
  var paths = this.getPaths();
  var path = new google.maps.MVCArray();
  if(sub == -1) {
    paths.push(path);
  } else {
    paths.setAt(sub, path);
  }
  var evtmap, evtpoly;
  var poly = this;
  evtmap = google.maps.event.addListener(this.get('map'), 'click', function(evt) {
    path.push(evt.latLng);
    if(path.getLength() > 2) {
      google.maps.event.removeListener(evtmap);
      google.maps.event.removeListener(evtpoly);
    }
  });
  
  evtpoly = google.maps.event.addListener(this, 'click', function(evt) {
    path.push(evt.latLng);
    if(path.getLength() > 2) {
      google.maps.event.removeListener(evtmap);
      google.maps.event.removeListener(evtpoly);
    }
  });
};

anthill.maps.Polygon.prototype.newSub = function() {
  this.new(-1);
};

anthill.maps.Polygon.prototype.getTmpPosition = function(index, n) {
  var path = this.get('axis').getAt(index);
  var p1 = path.getAt(n).getPosition();
  var p2 = path.getAt(n<path.getLength()-1?n+1:0).getPosition();
  var lat = (p1.lat()+p2.lat())/2;
  var lng = (p1.lng()+p2.lng())/2;
  return new google.maps.LatLng(lat, lng);
};

anthill.maps.Polygon.prototype.preparePaths = function(path, idx) {
  var poly = this;
  
  if(!path) {
    var paths = this.get('polygon').getPaths();
    if(paths && !paths.listener) {
      paths.listener = google.maps.event.addListener(paths, 'length_changed', function() {
	this.forEach(function(path, index) {
	  if(!poly.get('axis').getAt(index)) {
	    poly.get('axis').push(new google.maps.MVCArray());
	    poly.get('axis_tmp').push(new google.maps.MVCArray());
	    poly.preparePaths(path, index);
	    google.maps.event.trigger(path, 'length_changed');
	  } else {
	    poly.preparePaths(path, index);
	  }
	  
	});
	while(poly.get('axis').getLength() > this.getLength()) {
	  var e = poly.get('axis').pop();
	  e.forEach(function(marker, index) {
	    marker.unbindAll();
	    marker.setMap(null);
	  });
	  e = poly.get('axis_tmp').pop();
	  e.forEach(function(marker, index) {
	    marker.unbindAll();
	    marker.setMap(null);
	  });
	}
      });
      google.maps.event.addListener(paths, 'set_at', function(index, elem) {
	    var path = this.getAt(index);
	    poly.preparePaths(path, index);
	    google.maps.event.trigger(path, 'length_changed');
      });
    }
  } else {
    if(!path.listener) {
      path.listener = google.maps.event.addListener(path, 'length_changed', function() {
	this.forEach(function(latlng, index) {
	  if(!poly.get('axis').getAt(idx).getAt(index)) {
	    var m = new google.maps.Marker({
		position: latlng,
		draggable:true,
		raiseOnDrag: false,
		icon: poly.editVertex
	    });
	    m.idx = idx;
	    m.bindTo('map', poly, 'edit_map');
	    poly.get('axis').getAt(idx).push(m);
	    
	    m.l = google.maps.event.addListener(m, 'drag', function(evt){
	      var p = poly.getPosition(this, poly.get('axis').getAt(idx));
	      poly.get('polygon').getPaths().getAt(idx).setAt(p, evt.latLng);
	    });

	    var m = new google.maps.Marker({
		draggable:true,
		raiseOnDrag: false,
		icon: poly.tmpVertex
	    });
	    m.idx = idx;
	    m.bindTo('map', poly, 'edit_map');
	    
	    poly.tMarkerDrag = function(evt) { 
	      var p = poly.getPosition(this, poly.get('axis_tmp').getAt(this.idx));
	      this.setIcon(poly.editVertex);
	      google.maps.event.removeListener(this.l);

	      var m = new google.maps.Marker({
		  draggable:true,
		  raiseOnDrag: false,
		  icon: poly.tmpVertex
	      });
	      m.idx = this.idx;
	      m.bindTo('map', poly, 'edit_map');
	      
	      m.l = google.maps.event.addListener(m, 'drag', poly.tMarkerDrag);

	      poly.get('axis_tmp').getAt(this.idx).setAt(p, m);
	      
	      m = new google.maps.Marker({
		  draggable:true,
		  raiseOnDrag: false,
		  icon: poly.tmpVertex
	      });
	      m.idx = this.idx;
	      m.bindTo('map', poly, 'edit_map');
	      
	      m.l = google.maps.event.addListener(m, 'drag', poly.tMarkerDrag);
	      
	      poly.get('axis_tmp').getAt(this.idx).insertAt(p+1, m);
	      
	      poly.get('axis').getAt(this.idx).insertAt(p+1, this);
	      poly.get('polygon').getPaths().getAt(this.idx).insertAt(p+1, evt.latLng);
	      this.l = google.maps.event.addListener(this, 'drag', function(evt){
		var p = poly.getPosition(this, poly.get('axis').getAt(this.idx));
		poly.get('polygon').getPaths().getAt(this.idx).setAt(p, evt.latLng);
	      });
	    };
	    
	    m.l = google.maps.event.addListener(m, 'drag', poly.tMarkerDrag);
	    
	    poly.get('axis_tmp').getAt(idx).push(m);
	    
	  } else {
	    poly.get('axis').getAt(idx).getAt(index).setPosition(latlng);
	  }
	});
	while(poly.get('axis').getAt(idx).getLength() > this.getLength()) {
	  var e = poly.get('axis').getAt(idx).pop();
	  e.unbindAll();
	  e.setMap(null);
	  e = poly.get('axis_tmp').getAt(idx).pop();
	  e.unbindAll();
	  e.setMap(null);
	}
	poly.get('axis_tmp').getAt(idx).forEach(function(marker, index) {
	  marker.setPosition(poly.getTmpPosition(idx, index));
	});
      });
      
      google.maps.event.addListener(path, 'set_at', function(index, elem) {
	var latlng = this.getAt(index);
	poly.get('axis').getAt(idx).getAt(index).setPosition(latlng);
	var n = index==0?poly.get('axis_tmp').getAt(idx).getLength()-1:index-1;
	poly.get('axis_tmp').getAt(idx).getAt(n).setPosition(poly.getTmpPosition(idx, n));
	poly.get('axis_tmp').getAt(idx).getAt(index).setPosition(poly.getTmpPosition(idx, index));
      });
    }
  }
};

anthill.maps.Polygon.prototype.getPath = function() {
  return this.get('polygon').getPath();
};

anthill.maps.Polygon.prototype.getPaths = function() {
  return this.get('polygon').getPaths();
};

anthill.maps.Polygon.prototype.setPath = function(path) {
  this.get('polygon').setPath(path);
  this.preparePaths(path, 0);
  google.maps.event.trigger(path, 'length_changed');
};

anthill.maps.Polygon.prototype.setPaths = function(paths) {
  this.get('polygon').setPaths(paths);
  this.preparePaths();
  google.maps.event.trigger(paths, 'length_changed');
  paths.forEach(function(path, index) {
    google.maps.event.trigger(path, 'length_changed');
  });
};

anthill.maps.Polygon.prototype.getMap = function() {
  return this.get('map');
};

anthill.maps.Polygon.prototype.setMap = function(map) {
  this.set('map', map);
  if(this.get('editable') && this.get('editing')) this.set('edit_map', map);
};

anthill.maps.Polygon.prototype.setOptions = function(options) {
  this.get('polygon').setOptions(options);
};

anthill.maps.Polygon.prototype.edit = function(force) {
  if(!force && force !== false) {
    if(!this.get('editable') || this.get('editing')) {
      this.set('edit_map', null);
      this.set('editing', false);
    } else {
      this.set('edit_map', this.get('map'));
      this.set('editing', true);
    }
  } else {
    if(!this.get('editable') || !force) {
      this.set('edit_map', null);
      this.set('editing', false);
    } else {
      this.set('edit_map', this.get('map'));
      this.set('editing', true);
    }
  }
};
*/



/*********************************************************************\
*                                                                     *
* Adapted from the excelent algorithm in epolys.js by Mike Williams   *
*                                                                     *
/*********************************************************************/
google.maps.Polygon.prototype.contains = function(point) {
  var j=0;
  var x = point.lng();
  var y = point.lat();
  var paths = this.getPaths();
  var result = false;
  for(var z=0; z < paths.getLength(); z++) {
    var path = paths.getAt(z);
    var oddNodes = false;
    for (var i=0; i < path.getLength(); i++) {
      j++;
      if (j == path.getLength()) {j = 0;}
      if (((path.getAt(i).lat() < y) && (path.getAt(j).lat() >= y))
      || ((path.getAt(j).lat() < y) && (path.getAt(i).lat() >= y))) {
	if ( path.getAt(i).lng() + (y - path.getAt(i).lat())
	/  (path.getAt(j).lat()-path.getAt(i).lat())
	*  (path.getAt(j).lng() - path.getAt(i).lng())<x ) {
	  oddNodes = !oddNodes
	}
      }
    }
    result = result^oddNodes;
  }
  return !!result;
}

google.maps.Polygon.prototype.containedPolygonPoints = function(polygon) {
  var poly_paths = polygon.getPaths();
  var res_array = [];
  for(var i=0; i < poly_paths.getLength(); i++) {
    var poly_path = poly_paths.getAt(i);
    for(var j=0; j < poly_path.getLength(); j++) {
      if(this.contains(poly_path.getAt(j))) {
	res_array.push(poly_path.getAt(j));
      }
    }
  }
  return res_array;
}

google.maps.Polygon.prototype.containedPathPoints = function(path) {
  var res_array = [];
  for(var i=0; i < path.getLength(); i++) {
    if(this.contains(path.getAt(i))) {
      res_array.push(path.getAt(i));
    }
  }
  return res_array;
}

google.maps.Polygon.prototype.clipOperation = {
  INTERSECTION: 'intersection',
  DIFFERENCE: 'difference',
  UNION: 'union',
  XOR: 'xor'
};

google.maps.Polygon.prototype.clipKind = {
  CLIPPER: 'clipper',
  SUBJECT: 'subject'
};

google.maps.Polygon.prototype.clip = function(options) {
  if(options.polygon && !this.clipping) {
    this.clipping = true;
    options.operation = options.operation || this.clipOperation.INTERSECTION;
    options.kind = options.kind || this.clipKind.SUBJECT;
    
    if(options.kind == this.clipKind.SUBJECT) {
      var sub = this;
      var clip = options.polygon;
    } else {
      var sub = options.polygon;
      var clip = this;
    }
    var s_paths = sub.getPaths();
    var c_paths = clip.getPaths();

    var newPaths = new google.maps.MVCArray();
    
    var projection = this.getMap().get('overlayProjection');
    
    for(var i = 0; i < s_paths.getLength(); i++) {
      var s_path = s_paths.getAt(i);
      var has_intersection = false;
      var in_a_poly = false;
      for(var j = 0; j < c_paths.getLength(); j++) {
	var c_path = c_paths.getAt(j);
	//Phase one
	var s_points = [];
	var c_points = [];
	var intersects = false;
	for(var k = 0; k < s_path.getLength(); k++) {
	  var p = s_path.getAt(k);
	  s_points.push({
	    type: 's',
	    latlng: p,
	    proj: projection.fromLatLngToContainerPixel(p),
	    next: null,
	    prev: null,
	    nextOrig: null,
	    prevOrig: null,
	    nextPoly: null,
	    neighbor: null,
	    intersects: false,
	    border: false,
	    entry: false,
	    visited: false,
	    i: k
	  });
	  if(k > 0) {
	    s_points[k].prev = s_points[k-1];
	    s_points[k-1].next = s_points[k];
	    s_points[k].prevOrig = s_points[k-1];
	    s_points[k-1].nextOrig = s_points[k];
	  }
	  if(k == s_path.getLength()-1) {
	    s_points[k].next = s_points[0];
	    s_points[0].prev = s_points[k];
	    s_points[k].nextOrig = s_points[0];
	    s_points[0].prevOrig = s_points[k];
	  }
	}
	for(var k = 0; k < c_path.getLength(); k++) {
	  var p = c_path.getAt(k);
	  c_points.push({
	    type: 'c',
	    latlng: p,
	    proj: projection.fromLatLngToContainerPixel(p),
	    next: null,
	    prev: null,
	    nextOrig: null,
	    prevOrig: null,
	    nextPoly: null,
	    neighbor: null,
	    intersects: false,
	    border: false,
	    entry: false,
	    visited: false,
	    i: k
	  });
	  if(k > 0) {
	    c_points[k].prev = c_points[k-1];
	    c_points[k-1].next = c_points[k];
	    c_points[k].prevOrig = c_points[k-1];
	    c_points[k-1].nextOrig = c_points[k];
	  }
	  if(k == c_path.getLength()-1) {
	    c_points[k].next = c_points[0];
	    c_points[0].prev = c_points[k];
	    c_points[k].nextOrig = c_points[0];
	    c_points[0].prevOrig = c_points[k];
	  }
	}
	for(var l = 0; l < c_points.length; l++) {
	  var c_sp = c_points[l];
	  var c_ep = c_points[l+1 == c_points.length?0:l+1];
	  var c_start = c_sp.proj;
	  var c_end = c_ep.proj;
	  for(var k = 0; k < s_points.length; k++) {
	    var s_sp = s_points[k];
	    var s_ep = s_points[k+1 == s_points.length?0:k+1];
	    var s_start = s_sp.proj;
	    var s_end = s_ep.proj;
	  
	    var d = ((s_end.x - s_start.x) * (c_end.y - c_start.y)) - ((s_end.y - s_start.y) * (c_end.x - c_start.x));
	    
	    var n1 = ((s_start.y - c_start.y) * (c_end.x - c_start.x)) - ((s_start.x - c_start.x) * (c_end.y - c_start.y));
	    var r = n1 / d;
	    var n2 = ((s_start.y - c_start.y) * (s_end.x - s_start.x)) - ((s_start.x - c_start.x) * (s_end.y - s_start.y));
	    var s = n2 / d;
	    
	    
	    var x = s_start.x + (r * (s_end.x - s_start.x));
	    var y = s_start.y + (r * (s_end.y - s_start.y));
	    
	    var v0 = new google.maps.Point(x, y);
	    var latlng = projection.fromContainerPixelToLatLng(v0);
	   
	    //var v0 = projection.fromLatLngToContainerPixel(latlng);
	    //var s1 = projection.fromLatLngToContainerPixel(s_start);
	    //var e1 = projection.fromLatLngToContainerPixel(s_end);
	    var dss = d!=0&&r!=0?Math.sqrt(Math.pow(v0.x - s_start.x, 2) + Math.pow(v0.y - s_start.y, 2)):clip.getClosestLatLngToSegment(s_sp.latlng, l, j).distance.px;
	    var dse = d!=0&&r!=0?Math.sqrt(Math.pow(v0.x - s_end.x, 2) + Math.pow(v0.y - s_end.y, 2)):clip.getClosestLatLngToSegment(s_ep.latlng, l, j).distance.px;
	    var dcs = d!=0&&r!=0?Math.sqrt(Math.pow(v0.x - c_start.x, 2) + Math.pow(v0.y - c_start.y, 2)):sub.getClosestLatLngToSegment(c_sp.latlng, k, i).distance.px;
	    var dce = d!=0&&r!=0?Math.sqrt(Math.pow(v0.x - c_end.x, 2) + Math.pow(v0.y - c_end.y, 2)):sub.getClosestLatLngToSegment(c_ep.latlng, k, i).distance.px;
	    if(dss < 0.1 || dse < 0.1 || dcs < 0.1 || dce < 0.1) {
	      if(dss < 0.1 && !s_sp.border) {
		s_sp.border = true;
		if(Math.sqrt(Math.pow(c_start.x - s_start.x, 2) + Math.pow(c_start.y - s_start.y, 2)) < 0.1) {
		  c_sp.border = true;
		  s_sp.neighbor = c_sp;
		  c_sp.neighbor = s_sp;
		  s_sp.ds = dss;
		  s_sp.de = dse;
		  c_sp.ds = dcs;
		  c_sp.de = dce;
		} else if (Math.sqrt(Math.pow(c_end.x - s_start.x, 2) + Math.pow(c_end.y - s_start.y, 2)) < 0.1) {
		  c_ep.border = true;
		  s_sp.neighbor = c_ep;
		  c_ep.neighbor = s_sp;
		  s_sp.ds = dss;
		  s_sp.de = dse;
		  c_sp.ds = dcs;
		  c_sp.de = dce;
		} else {
		  var c_prime = {};
		  for(var el in s_sp) {
		    c_prime[el] = s_sp[el];
		  }
		  c_prime.neighbor = s_sp;
		  s_sp.neighbor = c_prime;
		  var c_last = c_ep.prev;
		  c_last.next = c_prime;
		  c_prime.prev = c_last;
		  c_ep.prev = c_prime;
		  c_prime.next = c_ep;
		  c_prime.ds = dcs;
		  s_sp.ds = dss;
		  c_prime.de = dce;
		  s_sp.de = dse;
		}
	      }
	      /*if(de < 0.1) {
		s_ep.border = true;
		var l = {};
		for(var i in s_ep) {
		  l[i] = s_ep[i];
		}
		l.neighbor = s_ep;
		s_sp.neighbor = l;
		var c_last = c_ep.prev;
		c_last.next = l;
		l.prev = c_last;
		c_ep.prev = l;
		l.next = c_ep;
	      }*/
	      continue;
	    }
	    
	    if(d==0) continue;
	   
	    if((r < 0 || r > 1) || (s < 0 || s > 1)) continue;
	   
	    var s_int = {
	      type: 's',
	      latlng: latlng, 
	      next: null,
	      prev: null,
	      nextPoly: null,
	      neighbor: null,
	      intersects: true,
	      border: false,
	      entry: false,
	      visited: false,
	      ds: dss,
	      de: dse
	    };
	    
	    var c_int = {
	      type: 'c',
	      latlng: latlng, 
	      next: null,
	      prev: null,
	      nextPoly: null,
	      neighbor: s_int,
	      intersects: true,
	      border: false,
	      entry: false,
	      visited: false,
	      ds: dcs,
	      de: dce
	    };
	    
	    s_int.neighbor = c_int;

	    if(s_int.ds <= s_int.de) {
		var init = s_sp;
		var tini = s_ep;
		var dir = 'next';
		var rid = 'prev';
		var dst = 'ds';
	    } else {
	      	var init = s_ep;
		var tini = s_sp;
		var dir = 'prev';
		var rid = 'next';
		var dst = 'de';
	    }
	    do {
	      var tmp = init[dir];
	      if(tmp == tini || (tmp.intersects && s_int[dst] <= tmp[dst])) {
		init[dir] = s_int;
		s_int[rid] = init;
		tmp[rid] = s_int;
		s_int[dir] = tmp;
		break;
	      }
	      init = tmp;
	    } while(init != tini)
	    
	    
	    if(c_int.ds <= c_int.de) {
		var init = c_sp;
		var tini = c_ep;
		var dir = 'next';
		var rid = 'prev';
		var dst = 'ds';
	    } else {
	      	var init = c_ep;
		var tini = c_sp;
		var dir = 'prev';
		var rid = 'next';
		var dst = 'de';
	    }
	    do {
	      var tmp = init[dir];
	      if(tmp == tini || (tmp.intersects && c_int[dst] <= tmp[dst])) {
		init[dir] = c_int;
		c_int[rid] = init;
		tmp[rid] = c_int;
		c_int[dir] = tmp;
		break;
	      }
	      init = tmp;
	    } while(init != tini)
	    
	    /*var s_last = s_ep.prev;
	    s_last.next = s_int;
	    s_int.prev = s_last;
	    s_ep.prev = s_int;
	    s_int.next = s_ep;
	    
	    var c_last = c_ep.prev;
	    c_last.next = c_int;
	    c_int.prev = c_last;
	    c_ep.prev = c_int;
	    c_int.next = c_ep;
	    */
	    intersects = true;
	    has_intersection = true;
	  }
	}
	//Phase two
	var par = this;	
	var test = function(point, poly, reverse, entry) {
	     var ret = {intersects: false, prev: false, next: false, entry: false};
	     
	     switch(options.operation) {
		case par.clipOperation.INTERSECTION:
		  if((!point.prev.border && poly.contains(point.prev.latlng) == reverse) || (!point.next.border && poly.contains(point.next.latlng) == reverse)) {
		    ret.intersects = true;
		  }
		  break;
		case par.clipOperation.DIFFERENCE:
		  if(!point.prevOrig.border && !point.nextOrig.border) {
		    ret.prev = point.prevOrig;
		    ret.next = point.nextOrig;
		    //return poly.contains(point.prevOrig.latlng) != poly.contains(point.nextOrig.latlng);
		  } else if (!point.prevOrig.border || !point.nextOrig.border) {
		    if(!point.prevOrig.border) {
		      var dir = 'nextOrig';
		      ret.prev = point.prevOrig;
		    } else {
		      var dir = 'prevOrig';
		      ret.next = point.nextOrig;
		    }
		    var tmpoint = point;
		    do {
		      tmpoint = tmpoint[dir];
		    } while(tmpoint.border && tmpoint != point);
		    //return poly.contains(opoint.latlng) != poly.contains(tmpoint.latlng);
		    if(ret.prev) {
		      ret.next = tmpoint;
		    } else {
		      ret.prev = tmpoint;
		    }
		  }
		  if(ret.prev!==false && ret.next!==false) {
		    var int = 0;
		    var pre = ret.prev;
		    do {
		      if(pre.intersects) {
			int++;
		      }
		      pre = pre.next;
		      if(pre == point) {
			ret.entry = (int % 2) == 0;
		      }
		    } while(pre != ret.next);
		      
		    if(poly.contains(ret.prev.latlng) != poly.contains(ret.next.latlng)) {
		      ret.intersects = (int % 2) == 0;
		      ret.entry = poly.contains(ret.next.latlng);
		    } else {
		      ret.intersects = (int % 2) != 0;
		    }
		  }
		  break;
		case par.clipOperation.UNION:
		  if((!point.prev.border && poly.contains(point.prev.latlng)) || (!point.next.border && poly.contains(point.next.latlng))) {
		    ret.intersects = true;
		  }
		  break;
		case par.clipOperation.XOR:
		  if((!point.prev.border && !poly.contains(point.prev.latlng)) || (!point.next.border && !poly.contains(point.next.latlng))) {
		    ret.intersects = true;
		  }
		  break;
	    }
	    return ret;
	  };
	
	var s_test_poly = new google.maps.Polygon({path: s_path});
	var c_test_poly = new google.maps.Polygon({path: c_path});
	
	//if(intersects) {
	  var s_p = s_points[0];
	  var c_p = c_points[0];
	  
	  switch(options.operation) {
		case this.clipOperation.INTERSECTION:
		  var s_last_entry = !c_test_poly.contains(s_p.latlng);
		  var c_last_entry = !s_test_poly.contains(c_p.latlng);
		  break;
		case this.clipOperation.DIFFERENCE:
		  if(s_p.border) {
		    var t = test(s_p, c_test_poly);
		    var s_last_entry = (t.intersects)?!t.entry:false;
		  } else {
		    var s_last_entry = c_test_poly.contains(s_p.latlng);
		  }
		  
		  if(c_p.border) {
		    var t = test(c_p, s_test_poly);
		    var c_last_entry = (t.intersects)?t.entry:false;
		  } else {
		    var c_last_entry = !s_test_poly.contains(c_p.latlng);
		  }
		  //var s_last_entry = c_test_poly.contains(s_p.latlng);
		  
		  break;
		case this.clipOperation.UNION:
		  var s_last_entry = c_test_poly.contains(s_p.latlng);
		  var c_last_entry = s_test_poly.contains(c_p.latlng);
		  break;
		case this.clipOperation.XOR:
		  var s_last_entry = !c_test_poly.contains(s_p.latlng);
		  var c_last_entry = s_test_poly.contains(c_p.latlng);
		  break;
	  }

	  do {
	    if(s_p.intersects) {
	      s_p.entry = s_last_entry;
	      s_last_entry = !s_last_entry;
	    } else if(s_p.border) {
		  if(test(s_p, c_test_poly, false, s_last_entry).intersects) {
		    s_p.intersects = true;
		    s_p.neighbor.intersects = true;
		    s_p.entry = s_last_entry;
		    s_last_entry = !s_last_entry;
		  }
	    }
	    s_p = s_p.next;
	  } while(s_p != s_points[0]);
	  
	  do {
	    if(c_p.intersects) {
	      c_p.entry = c_last_entry;
	      c_last_entry = !c_last_entry;
	    } else if(c_p.border) {
		  if(test(c_p, s_test_poly, true, c_last_entry).intersects) {
		    c_p.intersects = true;
		    c_p.entry = c_last_entry;
		    c_last_entry = !c_last_entry;
		  }
	    }
	    c_p = c_p.next;
	  } while(c_p != c_points[0]);
	//Phase three
	  var point = s_points[0];
	  var start = false;
	  while(point.next != s_points[0]) {
	    if(point.visited || (!start && !point.intersects)) {
	      point = point.next;
	      continue;
	    }
	    start = true;
	    if(point.intersects) {
	      var int_point = point;
	      var path = new google.maps.MVCArray();
	      var dir = '';
	      var push = false;
	      while(!int_point.visited) {
		for(var dir = int_point.entry?'next':'prev';;) {
		  if(push) path.push(int_point.latlng);
		  push = true;
		  int_point.visited = true;
		  int_point = int_point[dir];
		  if(int_point.intersects) {
		    path.push(int_point.latlng);
		    int_point.visited = true;
		    int_point = int_point.neighbor;
		    push = false;
		    break;
		  }
		}
	      }
	      newPaths.push(path);
	    }
	    point = point.next;
	  }
	/*} else {
	  var path = [];
	  switch(options.operation) {
		case this.clipOperation.INTERSECTION:
		  var has_out = false;
		  var s_p = s_points[0];
		  do {
		    if(s_p.border || c_test_poly.contains(s_p.latlng)) {
		      path.push(s_p.latlng)
		    } else {
		      has_out = true;
		    }
		    s_p = s_p.next;
		  } while(s_p != s_points[0])
		  if(has_out && path.length) {
		    newPaths.push(path);
		  }
		  break;
	  }
	}*/
      }
      switch(options.operation) {
	case this.clipOperation.INTERSECTION:
	  break;
	case this.clipOperation.DIFFERENCE:
	  if(!has_intersection && clip.containedPathPoints(s_path).length == 0) {
	    newPaths.push(s_path);
	  }
	  break;
	case this.clipOperation.UNION:
	  break;
	case this.clipOperation.XOR:
	  break;
      }
    }
    if(newPaths.getLength()) {
      sub.setPaths(newPaths);
      var ed = sub.getEditable();
      sub.setEditable(!ed);
      sub.setEditable(ed);
    }
    this.clipping = false;
  }
};

google.maps.Polygon.prototype.pathIntersects = function (path) {
  var t_paths = this.getPaths();
  var intersects = [];
  for(var i = 0; i < t_paths.getLength(); i++) {
    var t_path = t_paths.getAt(i);
    for(var j = 0; j < t_path.getLength(); j++) {
      var t_start = t_path.getAt(j);
      var t_end = t_path.getAt(j+1 == t_path.getLength()?0:j+1);
      for(var l = 0; l < path.getLength() - 1; l++) {
	var p_start = path.getAt(l);
	var p_end = path.getAt(l+1);
	var d = ((t_end.lng() - t_start.lng()) * (p_end.lat() - p_start.lat())) - ((t_end.lat() - t_start.lat()) * (p_end.lng() - p_start.lng()));
	if(d==0) continue;
	var n1 = ((t_start.lat() - p_start.lat()) * (p_end.lng() - p_start.lng())) - ((t_start.lng() - p_start.lng()) * (p_end.lat() - p_start.lat()));
	var r = n1 / d;
	var n2 = ((t_start.lat() - p_start.lat()) * (t_end.lng() - t_start.lng())) - ((t_start.lng() - p_start.lng()) * (t_end.lat() - t_start.lat()));
	var s = n2 / d;
	if((r < 0 || r > 1) || (s < 0 || s > 1)) continue;
	var lng = t_start.lng() + (r * (t_end.lng() - t_start.lng()));
	var lat = t_start.lat() + (r * (t_end.lat() - t_start.lat()));
	intersects.push({latlng: new google.maps.LatLng(lat, lng), path: t_path, start: t_start, end: t_end, sIndex: j, eIndex: j+1 == t_path.getLength()?0:j+1});
      }
    }
  }
  return intersects;
};

google.maps.Polygon.prototype.intersects = function(polygon) {
  var t_paths = this.getPaths();
  var p_paths = polygon.getPaths();
  var intersects = [];
  for(var i = 0; i < t_paths.getLength(); i++) {
    var t_path = t_paths.getAt(i);
    for(var j = 0; j < t_path.getLength(); j++) {
      var t_start = t_path.getAt(j);
      var t_end = t_path.getAt(j+1 == t_path.getLength()?0:j+1);
      for(var k = 0; k < p_paths.getLength(); k++) {
	var p_path = p_paths.getAt(k);
	for(var l = 0; l < p_path.getLength(); l++) {
	  var p_start = p_path.getAt(l);
	  var p_end = p_path.getAt(l+1 == p_path.getLength()?0:l+1);
	  var d = ((t_end.lng() - t_start.lng()) * (p_end.lat() - p_start.lat())) - ((t_end.lat() - t_start.lat()) * (p_end.lng() - p_start.lng()));
	  if(d==0) continue;
	  var n1 = ((t_start.lat() - p_start.lat()) * (p_end.lng() - p_start.lng())) - ((t_start.lng() - p_start.lng()) * (p_end.lat() - p_start.lat()));
	  var r = n1 / d;
	  var n2 = ((t_start.lat() - p_start.lat()) * (t_end.lng() - t_start.lng())) - ((t_start.lng() - p_start.lng()) * (t_end.lat() - t_start.lat()));
	  var s = n2 / d;
	  if((r < 0 || r > 1) || (s < 0 || s > 1)) continue;
	  var lng = t_start.lng() + (r * (t_end.lng() - t_start.lng()));
	  var lat = t_start.lat() + (r * (t_end.lat() - t_start.lat()));
	  intersects.push({latlng: new google.maps.LatLng(lat, lng), path: t_path, start: t_start, end: t_end, sIndex: j, eIndex: j+1 == t_path.getLength()?0:j+1});
	}
      }
    }
  }
  return intersects;
};

google.maps.Polygon.prototype.googleSetPaths = google.maps.Polygon.prototype.setPaths;

google.maps.Polygon.prototype.setPaths = function(paths) {
  this.googleSetPaths(paths);
  paths = this.getPaths();
  var polygon = this;
  var is = function(i) {
    var path = this;
    google.maps.event.trigger(polygon, 'edit', {path: path, index: i, new: path.getAt(i), old: false});
  };
  var st = function(i, e) {
    var path = this;
    google.maps.event.trigger(polygon, 'edit', {path: path, index: i, new: path.getAt(i), old: e});
  }
  var rm = function(i, e) {
    var path = this;
    google.maps.event.trigger(polygon, 'edit', {path: path, index: i, new: false, old: e});
  }
  var g = function(i) {
    google.maps.event.addListener(this.getAt(i), 'insert_at', is);
    google.maps.event.addListener(this.getAt(i), 'set_at', st);
    google.maps.event.addListener(this.getAt(i), 'remove_at', rm);
  };
  google.maps.event.addListener(paths, 'insert_at', g);
  google.maps.event.addListener(paths, 'set_at', g);
  for(var i = 0; i<paths.getLength(); i++) {
    g.call(paths, i);
  }
  paths.lst = true;
};

google.maps.Polygon.prototype.googleSetPath = google.maps.Polygon.prototype.setPath;

google.maps.Polygon.prototype.setPath = function(path) {
  this.googleSetPath(path);
  var paths = this.getPaths();
  if(!paths.lst) {
    var polygon = this;
    var is = function(i) {
      var path = this;
      google.maps.event.trigger(polygon, 'edit', {path: path, index: i, new: path.getAt(i), old: false});
    };
    var st = function(i, e) {
      var path = this;
      google.maps.event.trigger(polygon, 'edit', {path: path, index: i, new: path.getAt(i), old: e});
    }
    var rm = function(i, e) {
      var path = this;
      google.maps.event.trigger(polygon, 'edit', {path: path, index: i, new: false, old: e});
    }
    var g = function(i) {
      google.maps.event.addListener(this.getAt(i), 'insert_at', is);
      google.maps.event.addListener(this.getAt(i), 'set_at', st);
      google.maps.event.addListener(this.getAt(i), 'remove_at', rm);
    };
    google.maps.event.addListener(paths, 'insert_at', g);
    google.maps.event.addListener(paths, 'set_at', g);
    for(var i = 0; i<paths.getLength(); i++) {
      g.call(paths, i);
    }
    paths.lst = true;
  }
}

google.maps.Polygon.prototype.getClosestLatLngToSegment = function(latlng, j, i) {
  var projection = this.getMap().get('overlayProjection');
  var pos = projection.fromLatLngToContainerPixel(latlng);
  i = i || 0;
  j = j || 0;
  var path = this.getPaths().getAt(i);
  var dist;
  var x, y;
  var v0 = projection.fromLatLngToContainerPixel(path.getAt(j));
  var v1 = projection.fromLatLngToContainerPixel(path.getAt(j+1==path.getLength()?0:j+1));
  var vx = v0.x - pos.x;
  var vy = v0.y - pos.y;
  var ux = v1.x - v0.x;
  var uy = v1.y - v0.y;
  var length = Math.pow(ux, 2) + Math.pow(uy, 2);
  var det1 = (-vx * ux) + (-vy * uy); //if this is < 0 or > length then its outside the line segment
  if (det1 < 0) {
    dist = Math.sqrt(Math.pow(v0.x - pos.x, 2) + Math.pow(v0.y - pos.y, 2));
    y = v0.y;
    x = v0.x;
  } else if (det1 > length) {
    dist = Math.sqrt(Math.pow(v1.x - pos.x, 2) + Math.pow(v1.y - pos.y, 2));
    y = v1.y;
    x = v1.x;
  } else {
    var det2 = ux * vy - uy * vx;
    dist = Math.sqrt(Math.pow(det2, 2) / length);
    var hipot = Math.pow(vx, 2) + Math.pow(vy, 2);
    var to = Math.sqrt((hipot - Math.pow(dist, 2)) / length);
    var x = v0.x - (-ux * to);
    var y = v0.y - (-uy * to);
  }
  return {
    latlng: projection.fromContainerPixelToLatLng(new google.maps.Point(x, y)),
    distance: {px: dist}
  };
};

google.maps.Polygon.prototype.getClosestLatLng = function(latlng) {
  var paths = this.getPaths();
  var minDist; 
  var Y, X;
  var projection = this.getMap().get('overlayProjection');
  var pos = projection.fromLatLngToContainerPixel(latlng);
  for(var i = 0; i < paths.getLength(); i++) {
    var path = paths.getAt(i);
    for(var j = 0; j < path.getLength(); j++) {
      var dist;
      var x, y;
      var v0 = projection.fromLatLngToContainerPixel(path.getAt(j));
      var v1 = projection.fromLatLngToContainerPixel(path.getAt(j+1==path.getLength()?0:j+1));
      var vx = v0.x - pos.x;
      var vy = v0.y - pos.y;
      var ux = v1.x - v0.x;
      var uy = v1.y - v0.y;
      var length = Math.pow(ux, 2) + Math.pow(uy, 2);
      var det1 = (-vx * ux) + (-vy * uy); //if this is < 0 or > length then its outside the line segment
      if (det1 < 0) {
	dist = Math.sqrt(Math.pow(v0.x - pos.x, 2) + Math.pow(v0.y - pos.y, 2));
	y = v0.y;
	x = v0.x;
      } else if (det1 > length) {
	dist = Math.sqrt(Math.pow(v1.x - pos.x, 2) + Math.pow(v1.y - pos.y, 2));
	y = v1.y;
	x = v1.x;
      } else {
	var det2 = ux * vy - uy * vx;
	dist = Math.sqrt(Math.pow(det2, 2) / length);
	var hipot = Math.pow(vx, 2) + Math.pow(vy, 2);
	var to = Math.sqrt((hipot - Math.pow(dist, 2)) / length);
	var x = v0.x - (-ux * to);
	var y = v0.y - (-uy * to);
      }
      if(!minDist || dist < minDist) {
	minDist = dist;
	X = x;
	Y = y;
      }
    }
  }
  return {
    latlng: projection.fromContainerPixelToLatLng(new google.maps.Point(X, Y)),
    distance: {px: minDist}
  };
}